/**
 * Collaborative Story Development Web Application (CSDApp) 
 * Copyright Anton Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The notification template controller handles the end user's interaction with
 * CRUD management of notification templates.
 *
 * @author Anton Strack
 */
public class NotificationTemplateController extends ResourceController {

    private final String VIEW_LIST = "/notificationtemplate/index.jsp";
    private final String VIEW_VIEW = "/notificationtemplate/view.jsp";
    private final String VIEW_CREATE = "/notificationtemplate/create.jsp";
    private final String VIEW_UPDATE = "/notificationtemplate/update.jsp";
    private final String VIEW_DELTE = "/notificationtemplate/delete.jsp";

    public NotificationTemplateController(Connection conn) {
        super(conn);
    }

    /**
     * This method primarily handles form related requests.
     *
     * @param request the request object
     * @return an updated request object sufficient to complete the request.
     */
    @Override
    public CsdwaRequest handlePostRequest(CsdwaRequest request) {
        String form = request.getFormId();
        String primaryAction = request.getPrimaryActionTypeName();
        String primaryResource = request.getPrimaryResourceTypeName();
        int id = request.getPrimaryResourceId();
        if (form.isEmpty() == false) {
            System.out.println("form name is:" + form);
            if (form.equalsIgnoreCase("create-notificationtemplate")) {
                request.removeFormId();
                request = handleCreate(request);
            } else if (form.equalsIgnoreCase("update-notificationtemplate")) {
                request.removeFormId();
                request = handleUpdate(request);
            } else if (form.equalsIgnoreCase("delete-notificationtemplate")) {
                request.removeFormId();
                request = handleDelete(request);
            }
        } else if (primaryResource.equalsIgnoreCase("notificationtemplate")) {
            String secondaryType = request.getSecondaryResourceTypeName();
            int secondaryId = request.getSecondaryResourceId();

        }
        return request;
    }

    /**
     * This method handles get requests. It accepts the primaryResouce and
     * primaryAction as variables to allow routing from the handleGetRequest
     * method.
     *
     * @param request the request object
     * @return an updated request object sufficient to complete the request.
     */
    @Override
    public CsdwaRequest handleGetRequest(CsdwaRequest request) {
        String primaryAction = request.getPrimaryActionTypeName();
        String primaryResource = request.getPrimaryResourceTypeName();
        String secondaryResource = request.getSecondaryResourceTypeName();
        String secondaryAction = request.getSecondaryActionTypeName();
        int secondaryId = request.getSecondaryResourceId();
        int id = request.getPrimaryResourceId();
        if ("notificationtemplate".equalsIgnoreCase(primaryResource)) {
            if ("view".equalsIgnoreCase(primaryAction) && id != 0) {

                NotificationTemplateDao ntdao = new NotificationTemplateDao(conn);
                ntdao.setUseRelatedMarkers(true);
                int result = ntdao.selectBeanById(id);
                if (result > 0) {
                    request.setBean(ntdao.getBean());
                    NotificationRecipientMapperDao nrmDao = new NotificationRecipientMapperDao(conn);
                    int recipientsCount = nrmDao.selectUsersByNotificationTemplateId(id, true);
                    if (recipientsCount > 0) {
                        request.setAttribute("recipients", nrmDao.getUserDao().getBeans());
                    }
                    request.setView(VIEW_VIEW);
                } else {
                    request.setMessage("The notification template couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("notificationtemplate");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }
            } else if ("update".equalsIgnoreCase(primaryAction) && id != 0) {

                NotificationTemplateDao ntdao = new NotificationTemplateDao(conn);
                int result = ntdao.selectBeanById(id);
                if (result > 0) {
                    /* Store the bean in a session so that the it is available to the jsp page and also post update handler on post submit. This way it can compare the post bean values to the original bean values. Doing so will mean that only the columns changed will be sent to the database to be updated.
                     */
                    request.removeSessionAttribute("bean");
                    request.setSessionAttribute("bean", ntdao.getBean());
                    request.setView(VIEW_UPDATE);
                } else {
                    request.setMessage("The notification template couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("notificationtemplate");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }
            } else if ("create".equalsIgnoreCase(primaryAction)) {
                request.setView(VIEW_CREATE);

                UserDao udao = new UserDao(conn);
                ResourceTypeDao rtdao = new ResourceTypeDao(conn);
                TaskActionDao tadao = new TaskActionDao(conn);
                // select the users who don't have guest roles
                int uResult = udao.selectUsersByRole(InfoAggregator.getInstance().getRoleTypeId("guest"), false);
                if (uResult > 0) {
                    request.setAttribute("recipients", udao.getBeans());
                } else {
                    Logger.getLogger(NotificationTemplateController.class.getName()).log(Level.SEVERE, "No users could be found to populate the recipient field");
                }
                // select resource type
                // select by the parent id as this will get post type resources
                int rtResult = rtdao.selectResourceTypesByParentId(InfoAggregator.getInstance().getResourceTypeId("post"));
                if (rtResult > 0) {
                    // also get resource types of resource
                    rtResult = rtdao.selectResourceTypesByParentId(InfoAggregator.getInstance().getResourceTypeId("resource"));
                }
                Logger.getLogger(NotificationTemplateController.class.getName()).log(Level.INFO, "rtResult is:{0}", rtResult);
                if (rtResult > 0) {
                    request.setAttribute("resourceTypes", rtdao.getList());
                } else {
                    Logger.getLogger(NotificationTemplateController.class.getName()).log(Level.WARNING, "No resource type was detected for tasks.");
                }
                // select task actions
                request.setAttribute("actions", InfoAggregator.getInstance().getTaskActionTypes());
                StateDao sDao = new StateDao(this.conn);
                sDao.selectStateByResourceTypeId(InfoAggregator.getInstance().getResourceTypeId("fixedstate"));
                request.setAttribute("subjectStateTypes", sDao.getBeans());
            } else if ("delete".equalsIgnoreCase(primaryAction)) {

                NotificationTemplateDao ntdao = new NotificationTemplateDao(conn);
                int result = ntdao.selectBeanById(id);

                if (result > 0) {
                    request.setBean(ntdao.getBean());
                    request.setView(VIEW_DELTE);
                } else {
                    request.setMessage("The notification template couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("notificationtemplate");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }
            } else {// index by default
                request.setView(VIEW_LIST);
                NotificationTemplateDao ntdao = new NotificationTemplateDao(conn);
                NotificationRecipientMapperDao nrmDao = new NotificationRecipientMapperDao(conn);
                id = request.getCurrentUser().getId();
                nrmDao.setUseRelatedMarkers(true);
                int result = nrmDao.selectNotificationTemplatesByUserId(id, true);
                if (result <= 0) {
                    request.setMessage("There are no notification templates to list.");
                    request.setMessageType("info");
                }
                request.setBeans(nrmDao.getNotificationTemplateDao().getBeans());
            }
        }
        return request;
    }

    /**
     * This method saves the bean to the database.
     *
     * @param request the create request object containing the form values.
     * @return the request object with the requestResultMessage object set to
     * indicate the result
     */
    private CsdwaRequest handleCreate(CsdwaRequest request) {
        User user = (User) request.getSessionAttribute("user");
        Timestamp timestamp = new Timestamp(new Date().getTime());
        // validate fields

        CsdwaValidator isNotEmpty = new IsNotEmpty();
        CsdwaValidator isNumeric = new IsNumeric();
        // add request parameter values to the object to test validation
        ValidatorValue name = new ValidatorValue(request.getParameter("name"), "Name");
        ValidatorValue message = new ValidatorValue(request.getParameter("message"), "Message");
        ValidatorValue action = new ValidatorValue(request.getParameter("action"), "Notification Action");
        ValidatorValue resourceType = new ValidatorValue(request.getParameter("resourceType"), "Subject Resource Type");
        ValidatorValue includeCurrentUser = new ValidatorValue(request.getParameter("includeCurrentUser"), "Include Self");
        ValidatorValue subjectStateTypes = new ValidatorValue(request.getParameters("subjectStateType"), "Subject State Type");
        ValidatorValue recipients = new ValidatorValue(request.getParameters("recipient"), "Recipients");
        // the the validators to the validation values
        name.addValidator(isNotEmpty);
        message.addValidator(isNotEmpty);
        includeCurrentUser.addValidator(isNotEmpty);
        action.addValidator(isNumeric).addValidator(isNotEmpty);
        resourceType.addValidator(isNumeric).addValidator(isNotEmpty);
        subjectStateTypes.addValidator(isNumeric).addValidator(isNotEmpty);
        recipients.addValidator(isNumeric).addValidator(isNotEmpty);

        action.addValidator(isNumeric).addValidator(isNotEmpty);
        if (name.isValid() && message.isValid() && action.isValid() && resourceType.isValid() && subjectStateTypes.isValid() && recipients.isValid()) {
            try {
                boolean includeSelf = false;
                // if includeCurrentUser is empty or null then leave it at the default false.
                if (includeCurrentUser.isValid() == true) {
                    includeSelf = true;
                }
                //     NotificationTemplate bean = ResourceFactory.makeNotificationTemplate();
                // 2 loops
                // should create this as a transaction

                conn.setAutoCommit(false);// enter transaction mode
                Savepoint create = conn.setSavepoint("nt_create_" + action);
                NotificationTemplateDao ntdao = new NotificationTemplateDao(conn);
                NotificationRecipientMapperDao nrmDao = new NotificationRecipientMapperDao(conn);
                int result = 0;
                /* Begin inserting rows into the the Notificationtype table and if it succeeds then in the NotificationRecipientMap table. If there is any error then the code breaks out at the end of this label block and the transaction is rolled back. */
                insertRows:
                {
                    // loop though all selected actions for the current resourceType
                    int result2 = 0;
                    for (String subjectStateType : subjectStateTypes.getList()) {
                        NotificationTemplate bean = ResourceFactory.makeNotificationTemplate();
                        bean.setActionId(action.getInteger());
                        bean.setCreatedById(user.getId());
                        bean.setCreatedDate(timestamp);
                        bean.setMessage(message.getString());
                        bean.setName(name.getString());
                        bean.setSubjectResourceTypeId(resourceType.getInteger());
                        bean.setSubjectStateId(Integer.parseInt(subjectStateType));
                        bean.setIncludeCurrentUser(includeSelf);
                        result2 = ntdao.insertBean(bean);
                        if (result2 <= 0) {
                            result = 0;
                            request.setMessage("There was an error saving the notification template named: " + name + "to the database. Nothing was saved to the database.");
                            break insertRows; // error with inserting notification template. Rollback transaction
                        } else {
                            // loop through each recipient and create a the same notificaiton template for each of them.
                            for (String recipient : recipients.getList()) {
                                NotificationRecipientMap nr = new NotificationRecipientMap();
                                nr.setNotificationTypeId(result2);
                                nr.setUserId(Integer.parseInt(recipient));
                                result = nrmDao.insertBean(nr);
                                if (result <= 0) {
                                    request.setMessage("There was an error saving the notification recipient to the database. Nothing was saved to the database.");
                                    break insertRows; // error with inserting notification template. Rollback transaction
                                }
                            }
                        }
                    }
                }
                if (result > 0) {
                    conn.setAutoCommit(true); // commit the transaction and reset autocommit to the default
                    request.setMessage("The notification template was successfully created.");
                    request.setMessageType("success");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("notificationtemplate");
                    request.setPrimaryResourceId(result);
                    request = handleGetRequest(request);
                } else {
                    conn.rollback(create);
                    conn.setAutoCommit(true);// commit the transaction and reset autocommit to the default
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("create");
                    request.setPrimaryResourceTypeName("notificationtemplate");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }
            } catch (SQLException ex) {
                Logger.getLogger(NotificationTemplateController.class
                        .getName()).log(Level.SEVERE, null, ex);
                ex.printStackTrace();
            }
        } else {
            request.setMessage(name.getErrorMessage() + message.getErrorMessage() + action.getErrorMessage() + resourceType.getErrorMessage() + recipients.getErrorMessage() + subjectStateTypes.getErrorMessage() + includeCurrentUser.getErrorMessage());
            request.setMessageType("info");
            request.setPrimaryActionTypeName("create");
            request.setPrimaryResourceTypeName("notificationtemplate");
            request.setPrimaryResourceId(0);
            request = handleGetRequest(request);
        }
        return request;
    }

    /**
     * This method handles updating the bean. If the bean to be updated was
     * properly set as a bean in the session, then this method will instruct the
     * dao to update only the fields that were changed and thus reduce message
     * size.
     *
     * @param request the update request object
     * @return a request object with the requestResultMessage object set to
     * indicate the update result.
     */
    private CsdwaRequest handleUpdate(CsdwaRequest request) {
        User user = (User) request.getSessionAttribute("user");
        Timestamp timestamp = new Timestamp(new Date().getTime());
        NotificationTemplate originalPost = (NotificationTemplate) request.getSessionAttribute("bean");

        CsdwaValidator isNotEmpty = new IsNotEmpty();
        CsdwaValidator isNumeric = new IsNumeric();
        // add request parameter values to the object to test validation
        ValidatorValue name = new ValidatorValue(request.getParameter("name"), "Name");
        ValidatorValue message = new ValidatorValue(request.getParameter("message"), "Message");
        ValidatorValue includeCurrentUser = new ValidatorValue(request.getParameter("includeCurrentUser"), "Include Self");
        ValidatorValue id = new ValidatorValue(request.getParameter("id"), "ID");
        // the the validators to the validation values
        name.addValidator(isNotEmpty);
        message.addValidator(isNotEmpty);
        includeCurrentUser.addValidator(isNotEmpty);
        id.addValidator(isNumeric).addValidator(isNotEmpty);
        if (name.isValid() && message.isValid()) {
            NotificationTemplateDao ntdao = new NotificationTemplateDao(conn);
            StringBuilder sb = new StringBuilder();

            boolean includeSelf = false;
            // if includeCurrentUser is empty or null then leave it at the default false.
            if (includeCurrentUser.isValid() == true) {
                includeSelf = true;
            }

            if (originalPost == null) {
                originalPost = ResourceFactory.makeNotificationTemplate();// if the original post didn't get stored in the session, then create a new one.
            }

            if (originalPost.getName().equals(name.getString()) == false) {
                sb.append("Name");
            }
            if (originalPost.getMessage().equals(message.getString()) == false) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append("Message");
            }
            if (originalPost.isIncludeCurrentUser() != includeSelf) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append("IncludeCurrentUser");
            }

            // update the original bean with all the values from the form submit
            originalPost.setName(name.getString());
            originalPost.setMessage(message.getString());
            originalPost.setIncludeCurrentUser(includeSelf);

            // declare the columns to update.
            ntdao.setColumnsToUpdate(sb.toString());
            int result = ntdao.updateBean(originalPost);
            request.removeSessionAttribute("bean");
            if (result > 0) {
                //   this.getResourceRequestDelegator().getUserHistoryManager().recordUserPostHistory(request.getCurrentUser(), getResourceRequestDelegator().getActionId("update"), originalPost);
                // success so go to view page of updated plot
                request.setMessage("The notification template was successfully updated.");
                request.setMessageType("success");
                request.setPrimaryActionTypeName("view");
                request.setPrimaryResourceTypeName("notificationtemplate");
                request.setPrimaryResourceId(originalPost.getId());
                request = handleGetRequest(request);
            } else {
                request.setMessage("There was an error trying to update the notification template to the database.");
                request.setMessageType("error");
                request.setPrimaryActionTypeName("update");
                request.setPrimaryResourceTypeName("notificationtemplate");
                request.setPrimaryResourceId(originalPost.getId());
                request = handleGetRequest(request);
            }
        } else {
            request.setMessage(name.getErrorMessage() + message.getErrorMessage() + includeCurrentUser.getErrorMessage());
            request.setMessageType("error");
            request.setPrimaryActionTypeName("update");
            request.setPrimaryResourceTypeName("notificationtemplate");
            request.setPrimaryResourceId(originalPost.getId());
            request = handleGetRequest(request);
        }
        return request;
    }

    /**
     * This method handles deleting the specific bean from the database. This
     * method handles the cascading deletes of all dependant resources.
     *
     * @param request the delete request object.
     * @return a request object with a requestResultMessage object specifying
     * the result.
     */
    private CsdwaRequest handleDelete(CsdwaRequest request) {
        // validate fields
        int id = request.getPrimaryResourceId();
        boolean allowDelete = false;
        if (id > 0) {
            try {
                // set auto commit off for connection

                conn.setAutoCommit(false);// enter transaction mode
                Savepoint delete = conn.setSavepoint("delete-notificationtemplate_" + id);
                NotificationTemplateDao ntdao = new NotificationTemplateDao(conn);
                if (ntdao.selectBeanById(id) > 0) {
                    allowDelete = true;
                }
                if (allowDelete == true) {
                    // delete recipient rows.
                    NotificationRecipientMapperDao nrmDao = new NotificationRecipientMapperDao(conn);
                    nrmDao.selectBeansByNotifcationTemplateId(id);
                    List<NotificationRecipientMap> nrs = nrmDao.getList();
                    Iterator<NotificationRecipientMap> beans = nrs.iterator();
                    while (beans.hasNext() && allowDelete == true) {
                        NotificationRecipientMap nr = beans.next();
                        int result = nrmDao.deleteBean(nr);
                        if (result <= 0) {
                            allowDelete = false;
                        }
                    }
                }
                if (allowDelete == true) {
                    NotificationTemplate nt = ntdao.getBean();
                    int result = ntdao.deleteBean(nt);
                    request.removeAttribute("formSubmit");
                    if (result > 0) {
                        //this.getResourceRequestDelegator().getUserHistoryManager().recordUserPostHistory(request.getCurrentUser(), getResourceRequestDelegator().getActionId("delete"), task);
                        conn.setAutoCommit(true);// reset commit mode to auto and commit the transaction since the deletion worked correctly.
                        request.setMessage("The notification template was successfully deleted from the site.");
                        request.setMessageType("success");
                        request.setPrimaryActionTypeName("list");
                        request.setPrimaryResourceTypeName("notificationtemplate");
                        request.setPrimaryResourceId(0);
                        request = handleGetRequest(request);
                    } else {
                        conn.rollback(delete); // delete failed so roll back connection
                        conn.setAutoCommit(true);// reset commit mode to auto
                        request.setMessage("There was an error trying to delete the notification template from the database.");
                        request.setMessageType("error");
                        request.setPrimaryActionTypeName("view");
                        request.setPrimaryResourceTypeName("notificationtemplate");
                        request.setPrimaryResourceId(id);
                        request = handleGetRequest(request);
                    }

                } else {
                    conn.rollback(delete); // delete failed so roll back connection
                    conn.setAutoCommit(true);// reset commit mode to auto
                    request.setMessage("A dependant resource could not be deleted. Deletion aborted.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("view");
                    request.setPrimaryResourceTypeName("notificationtemplate");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }

            } catch (SQLException ex) {
                Logger.getLogger(NotificationTemplateController.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            request.setMessage("There was an error with the delete request. Could not determine the notification tempalte ID.");
            request.setMessageType("error");
            request.setPrimaryActionTypeName("list");
            request.setPrimaryResourceTypeName("notificationtemplate");
            request.setPrimaryResourceId(0);
            request = handleGetRequest(request);
        }

        return request;
    }
}
